package com.wuhao.code.check.model

import com.aegis.mybatis.xmlless.AnnotationSupport
import com.aegis.mybatis.xmlless.annotations.*
import com.aegis.mybatis.xmlless.annotations.criteria.Criteria
import com.aegis.mybatis.xmlless.constant.Annotations
import com.aegis.mybatis.xmlless.enums.Operations
import com.aegis.mybatis.xmlless.model.ChooseCriteriaInfo
import com.aegis.mybatis.xmlless.model.JoinObjectInfo
import com.aegis.mybatis.xmlless.model.base.BaseAnnotatedElementWrapper
import com.aegis.mybatis.xmlless.model.base.BaseClassWrapper
import com.intellij.psi.PsiElement
import com.wuhao.code.check.*
import org.apache.ibatis.annotations.Param
import org.apache.ibatis.type.TypeHandler
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import java.lang.reflect.Type

/**
 *
 * @author 吴昊
 * @date 2024/1/23 19:25
 * @since v0.0.0
 * @version 1.0
 */
abstract class BasePsiAnnotatedElementWrapper<T: PsiElement>(val element: T) : BaseAnnotatedElementWrapper {

  fun createChooseFromWhenList(): ChooseCriteriaInfo? {
    val whenList = element.getStringListAnnotationValue(ChooseCriteria::class.java.name, "value")
    if (whenList.isNotEmpty()) {
      return ChooseCriteriaInfo(
          whenList.map {
            this.parseTestCriteria(it)
          },
          element.getStringAnnoValue(ChooseCriteria::class.java.name, ChooseCriteria::otherwise.name) ?: ""
      )
    }
    return null
  }

  override fun getSpecificName(): String? {
    return element.getStringAnnoValue(
        Param::class.java.name
    )
  }

  override fun getPropertyMapTo(): String {
    return element.getStringAnnoValue(
        PropertyMapping::class.java, PropertyMapping::value
    ) ?: ""
  }

  fun resolveJoinObjectFromKtAnnotation(it: KtAnnotationEntry): JoinObjectInfo {
    var joinOnTargetProperty = ""
    var joinOnThisProperty = ""
    var alias = ""
    lateinit var targetClassWrapper: BaseClassWrapper
    it.valueArguments.forEach {
      val name = it.getArgumentName()?.asName?.asString()
      when (name) {
        FeignProperty::joinOnTargetProperty.name ->
          joinOnTargetProperty = it.getArgumentExpression()?.toStringValue() ?: ""

        FeignProperty::joinOnThisProperty.name   ->
          joinOnThisProperty = it.getArgumentExpression()?.toStringValue() ?: ""

        FeignProperty::alias.name                -> alias = it.getArgumentExpression()?.toStringValue() ?: ""
        FeignProperty::targetEntity.name         -> {
          val ktClass = resolveKtClass(it.getArgumentExpression()) ?: error("无法解析${it.getArgumentExpression()}")
          targetClassWrapper = KtClassWrapper(ktClass)
        }
      }
    }
    return JoinObjectInfo(joinOnTargetProperty, joinOnThisProperty, alias, targetClassWrapper)
  }

  override fun getUpdate(): String? {
    return element.getStringAnnoValue(Annotations.Plus.TABLE_FIELD, "update")
  }

  override fun getCustomTypeHandlerClass(): Class<out TypeHandler<*>>? {
    return null
  }

  override fun getCriteria(): Criteria? {
    if (!element.hasAnnotation(Criteria::class.java)) {
      return null
    }
    val properties = element.getStringListAnnotationValue(Criteria::class.java.name, "properties")
    val value = Operations.valueOf(
        element.getStringAnnoValue(
            Criteria::class.java.name,
            "value"
        ) ?: Operations.Eq.name
    )
    return Criteria(value, properties.toTypedArray())
  }

  override fun getGenericType(): Type {
    return Any::class.java
  }

  override fun isNoIfWrap(): Boolean {
    return element.hasAnnotation(NoIfWrap::class.java)
  }

  override fun isJsonMappingProperty(): Boolean {
    return element.hasAnnotation(JsonMappingProperty::class.java)
  }

  override fun isSelectIgnore(): Boolean {
    return isTransient() || element.hasAnnotation(Count::class.java.name)
  }

  override fun isUpdateIgnore(): Boolean {
    return isTransient() || AnnotationSupport.ALL_COLUMN_ANNOTATIONS.any {
      element.getStringAnnoValue(it, "updatable") == "false"
    } || element.hasAnyAnnotation(
        Count::class.java.name,
        JoinObject::class.java.name,
        JoinTableColumn::class.java.name,
        Annotations.CREATED_DATE,
        Annotations.CREATED_BY
    )
  }

  override fun isAutoGenerateKey(): Boolean {
    return AnnotationSupport.GENERATED_VALUE_ANNOTATIONS.any {
      element.getStringAnnoValue(it, "strategy") == "SEQUENCE"
    }
  }

  override fun isCount(): Boolean {
    return element.hasAnnotation(Count::class.java)
  }

  override fun getElement(): Any {
    return element
  }

  override fun isNotNull(): Boolean {
    return AnnotationSupport.NOT_NULL_ANNOTATIONS.any {
      element.hasAnnotation(it)
    }
  }

  override fun isNotEmpty(): Boolean {
    return AnnotationSupport.NOT_EMPTY_ANNOTATIONS.any {
      element.hasAnnotation(it)
    }
  }

  override fun isNotBlank(): Boolean {
    return AnnotationSupport.NOT_BLANK_ANNOTATIONS.any {
      element.hasAnnotation(it)
    }
  }

  override fun isInsertIgnore(): Boolean {
    return isTransient() || AnnotationSupport.ALL_COLUMN_ANNOTATIONS.any {
      element.getStringAnnoValue(it, "insertable") == "false"
    } || element.hasAnyAnnotation(
        Count::class.java.name,
        JoinObject::class.java.name,
        JoinTableColumn::class.java.name
    )
  }

  private fun isTransient(): Boolean {
    return element.hasAnnotation("kotlin.jvm.Transient")
  }

}
